import sqlite3
import os
from threading import Thread
import time
import logging
from hash2 import *
from pubsub import pub #pypubsub
from hash_gui import *
from inc import *
import wx

class HashThread(Thread):
    def __init__(self,db,rootpath="e:",hashTable="hashlist",writeTable="hash",expirationDate=0):
        Thread.__init__(self)
        pub.subscribe(self.setflag,"stopHashThread")
        self.root=rootpath.strip("\\")
        self.begintime = time.time()
        self.db=db
        self.cur=db.cursor()
        self.hashTable=hashTable
        self.writeTalbe=writeTable
        expirationDate=int(expirationDate)
        if(expirationDate>0):
            self.expirationDate=expirationDate*86400
        else:
            self.expirationDate=expirationDate
        self.info={
            "root":self.root,
            "path":"",
            "complete":0,
            "dircount": 0, 
            "filecount": 0,
            "bar2":0,
            
            }
        self.sqlBlockSize=int(getConfig(db,"sqlBlockSize"))
        self.stopFlag=False
        self.start()
    def __del__(self):
        print(f"HashThread_del")

    def setflag(self,msg):
        self.stopFlag=msg
    def run(self):
        wx.CallAfter(pub.sendMessage,"hashThreadStatus",msg=True)
        self.uptextCtrl(f"开始{self.writeTalbe}")
        try:
            self.db.execute(f"DELETE FROM `{self.writeTalbe}`  WHERE hash = '文件无法打开' OR hash = 'fail!'")
        except (sqlite3.Error, Exception) as e:
            logging.error("Database UPDATE error: %s" % e)
        self.db.commit()
        if self.expirationDate<=0:
            csql=f"select count(`hash`) from `{self.writeTalbe}`"
        else:
            csql=f"select count(`hash`) from `{self.writeTalbe}` WHERE \"timestamp\" > {int(time.time()-self.expirationDate)}"
        r = self.cur.execute(csql)  # 完成任务量
        n = r.fetchone()
        self.info['complete']=n[0]
        r = self.cur.execute(f"select count(*) from `{self.hashTable}`")  # 文件总计
        n = r.fetchone()
        self.info['filecount']=n[0]    
        if self.info['filecount'] ==0 :
            msg("无文件！")
            wx.CallAfter(pub.sendMessage,"hashThreadStatus",msg=False)
            return False
        self.upinfo()
        if (self.expirationDate<=0) :
            rsql = f"select path from `{self.hashTable}` WHERE hash IS NULL limit 0,{self.sqlBlockSize}"
        else :
            rsql =f"select path from `{self.hashTable}` WHERE \"timestamp\" < {int(time.time()-self.expirationDate)} OR \"timestamp\" IS NULL OR hash IS NULL limit 0,{self.sqlBlockSize}"
        r = self.cur.execute(rsql)
        r=r.fetchall()
        while(len(r)>0):
            for i in r:
                self.info['path']=i[0]
                f=os.path.join(self.info['root']+"\\", i[0].strip("\\"))
                logging.info(f)
                if(os.path.isfile(f)):
                    hash=self.getHash(f)
                else:
                    logging.error(f"文件错误：{f}")
                    self.uptextCtrl(f"文件错误：{f}")
                    self.info['complete']=self.info['complete']+1
                    hash="文件无法打开"
                self.info['path']=self.info['path'].replace("\'", "\'\'")
                if (self.expirationDate>0 or self.expirationDate== -1) :
                    wsql=sql=f"REPLACE INTO `{self.writeTalbe}` (path,hash,timestamp) VALUES ('{self.info['path']}', '{hash}',{int(time.time())});"
                else:
                    wsql=f"REPLACE INTO `{self.writeTalbe}` (path,hash) VALUES ('{self.info['path']}', '{hash}');"
                self.db.execute(wsql)
                # self.db.commit()
                if(self.stopFlag):
                    self.uptextCtrl(f"暂停{self.writeTalbe}")
                    wx.CallAfter(pub.sendMessage,"hashThreadStatus",msg=False)
                    self.db.commit()
                    # self.db.close()
                    return False
                self.info['complete']=self.info['complete']+1
            self.db.commit()
            r=self.cur.execute(csql)
            r=r.fetchone()
            self.info['complete']=r[0]
            r = self.cur.execute(rsql)
            r=r.fetchall()
        self.uptextCtrl(f"完成{self.writeTalbe}")
        self.upinfo()
        wx.CallAfter(pub.sendMessage,"hashThreadStatus",msg=False)
        self.db.commit()
        # self.db.close()

    def upinfo(self):
        wx.CallAfter(pub.sendMessage,"upProgressBar1",msg=math.ceil(self.info["complete"]/self.info["filecount"]*100))
        wx.CallAfter(pub.sendMessage,"upProgressBar2",msg=self.info["bar2"])
        wx.CallAfter(pub.sendMessage,"upStaticText1",msg=f"文件总计：{self.info['filecount']}  已完成：{self.info['complete']}  时间：{time.time()-self.begintime:.2f}")
        wx.CallAfter(pub.sendMessage,"upStautsBar1",msg=self.info['path'])
    def uptextCtrl(self,msg):
        wx.CallAfter(pub.sendMessage,"uptextCtrl1",msg=f"{msg}")
    def getHash(self, filepath):
        if(os.path.isfile(filepath)):
            size = os.path.getsize(filepath)
            # print(file,size)
            try:
                file = open(filepath, "rb")
            except:
                logging.error(f"can't open file!\t{filepath}")
                wx.CallAfter(pub.sendMessage,"uptextCtrl1",msg=u"文件%s无法打开\n" % (filepath))
                # p.m_textCtrl1.AppendText(u"%s 文件%s无法打开\n" % (
                #     time.strftime('%Y-%m-%d %H:%M:%S'), filepath))
                return "fail!"
            hash = hash_()
            n = 0
            bs = 1024*1024*32
            ab = math.ceil(size/bs)
            while 1:
                # 处理该行的代码
                data = file.read(bs)
                if(len(data) == 0):
                    break
                if(self.stopFlag):
                    logging.info("exitThreading")
                    self.db.commit()
                    return "fail!"             
                hash.update(data)
                n = n+1
                if(ab > 2):
                    self.info["bar2"]=math.ceil(n/ab*100)
                elif ab == n:
                    self.info["bar2"]=0
                self.upinfo()
            file.close()
            return hash.hexdigest().upper()
            # return hash.hexdigest()
        else:
            print("error")

    def insertdb(self, path, hash, dbname, fs):
        # global db
        try:
            logging.info("INSERT INTO %s (path,hash) VALUES ('%s', '%s' )" % (
                dbname.replace("\'", "\'\'"), path.replace("\'", "\'\'"), hash))
            db.execute("INSERT INTO %s (path,hash) VALUES ('%s', '%s' )" % (
                dbname.replace("\'", "\'\'"), path.replace("\'", "\'\'"), hash))
        except (sqlite3.Error, Exception) as e:
            logging.error("Database INSERT error: %s" % e)
            # db.rollback()

########################################################################
class ScanThread(Thread):
    def __init__(self,rootpath):
        Thread.__init__(self)

        self.startTime = time.time()
        
        self.rootpath=rootpath
        self.db=sqlite3.connect(f"{self.rootpath}\\{logfilename}.db",check_same_thread=False)
        self.info={
            'filelist': [],
            'filecount' : 0,
            'dircount' :0,
            'complete': 0,
        }
        pub.subscribe(self.setflag,"stopHashThread")
        self.stopFlag=False
        self.start()
    def setflag(self,msg):
        self.stopFlag=msg
    def run(self):
        wx.CallAfter(pub.sendMessage,"hashThreadStatus",msg=True)
        wx.CallAfter(pub.sendMessage,"uptextCtrl1",msg=f"开始扫描{self.rootpath}")
        # wx.CallAfter(pub.sendMessage, "up", msg=info)
        
        # p.m_textCtrl1.AppendText("扫描目录："+self.path+"\n")
        self.scan()
        if (not trySql(self.db,f"DELETE FROM `list` WHERE (\"path\"=\'\\{logfilename}.db')")):
            logging.error(f"delete path \\{logfilename}.db error!")
            msg(f"\\{logfilename}.db 删除失败！")
        if (not trySql(self.db,r"delete from list where path like '\__breakfile__\%';")):
            logging.error(f"delete __breakfile__ error!")
            msg(f"__breakfile__ 删除失败！")
        wx.CallAfter(pub.sendMessage,"hashThreadStatus",msg=False)
        if(not self.stopFlag):
            wx.CallAfter(pub.sendMessage,"uptextCtrl1",msg=f"目录{self.rootpath}扫描完成")
        # wx.CallAfter(pub.sendMessage, "up", msg=info)

    def scan(self):
        for dir_path, subpaths, files in os.walk(self.rootpath, True):
            if(self.stopFlag):
                wx.CallAfter(pub.sendMessage,"uptextCtrl1",msg=f"目录{self.rootpath}扫描停止！")
                wx.CallAfter(pub.sendMessage,"hashThreadStatus",msg=False)
                self.db.commit()
                # self.db.close()
                return False
            try:
                rpath=dir_path[len(self.rootpath.rstrip("\\")):].replace("\'", "\'\'") if len(dir_path) != len(self.rootpath) else '\\'
                self.db.execute("INSERT INTO dirs (path,hash,checks) VALUES ('%s', '%s', '%s' )" % (rpath, '0', '0'))
            except (sqlite3.Error, Exception) as e:
                logging.info(dir_path)
                logging.error("Database dirs insert error: %s" % e)
                # p.m_textCtrl1.AppendText("error： %s \n" % dir_path)
                # self.db.rollback()
            wx.CallAfter(pub.sendMessage,"upStautsBar1",msg=dir_path)
            for file in files:
                # if(info["ThreadRun"] == False):
                #     print("exit")
                #     return False
                # if file == logfilename or file == logfilename+".db":  ###############################################
                #     continue
                file_path = os.path.join(dir_path, file)
                # info["file"] = file
                # info["path"] = dir_path
                # info['filelist'].append(file_path)
                logging.debug(file_path)
                try:
                    self.db.execute("INSERT INTO list (path,hash,checks) VALUES ('%s', '%s', '%s' )" % (
                        file_path[len(self.rootpath.rstrip("\\")):].replace("\'", "\'\'"), '0', '0'))
                except (sqlite3.Error, Exception) as e:
                    logging.info(file_path)
                    logging.error("Database error: %s" % e)
                    # p.m_textCtrl1.AppendText("error： %s \n" % file_path)
                    # self.db.rollback()
                self.info['filecount'] += 1
            wx.CallAfter(pub.sendMessage,"upStaticText1",msg=f"目录总数：{self.info['dircount'] }  文件总数：{self.info['filecount'] }")
            self.info['dircount'] += 1
            # wx.CallAfter(pub.sendMessage, "up", msg=info)
        self.db.commit()
        cur =self.db.cursor()
        # trySql()
        r = cur.execute("select count(path) from dirs")  #
        c = r.fetchone()
        self.info['dircount'] = c[0]
        r = cur.execute("select count(path) from list")  #
        c = r.fetchone()
        self.info['filecount'] = c[0]


class MyDataTable(grid.PyGridTableBase):
    def __init__(self, data, colLabels=None):
        grid.GridTableBase.__init__(self)
        self.colLabels = colLabels
        self.data = data

    def GetNumberRows(self):
        return len(self.data)

    def GetNumberCols(self):
        return len(self.data[0])

    def GetValue(self, row, col):
        if self.data[row][col] is None:
            return ' '
        else:
            return self.data[row][col]

    def SetValue(self, row, col, value):
        pass

    def GetColLabelValue(self, col):
        if self.colLabels != None:
            return self.colLabels[col]
        else:
            return "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[col]




if __name__ == "__main__":
    app=wx.App()
    db=sqlite3.connect(f"D:\\markpic\\{logfilename}.db",check_same_thread=False)
    test=HashThread(db=db,writeTable="checks",hashTable='checkslist',expirationDate=0.05)